package cn.sample.security.json.filter;

import cn.sample.common.properties.SysProperties;
import cn.sample.security.json.handler.JsonAuthenticationFailureHandler;
import cn.sample.system.bean.User;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * <h2></h2>
 *
 * <p>校验 Json 登录的 token </p>
 *
 * @author HKD
 */
@Component
public class JsonTokenAuthenticationFilter extends OncePerRequestFilter {

    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    @Resource
    private SysProperties sysProperties;
    @Resource
    private JsonAuthenticationFailureHandler authenticationFailureHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取请求头
        String headerToken = request.getHeader(HttpHeaders.AUTHORIZATION);

        if(StringUtils.isNoneBlank(headerToken)){
            String[] auths = headerToken.split(":");
            if(auths.length != 2){
                unsuccessfulAuthentication(request,response,new AccountExpiredException("请求异常。"));
                return;
            }
            String tokenCacheKey = StringUtils.join(sysProperties.getProject(),":security:auto:",auths[0],":",auths[1]);
            Long expire = redisTemplate.getExpire(tokenCacheKey);
            expire = expire == null ? 0L : expire;
            if(expire <= 0L){
                unsuccessfulAuthentication(request,response,new AccountExpiredException("登录已过期。"));
                return;
            }
            if(expire <= 100){
                // 重新设置过期时间
                redisTemplate.expire(tokenCacheKey,1800, TimeUnit.SECONDS);
            }

            User user = (User) redisTemplate.opsForValue().get(tokenCacheKey);

            // 封装认证成功请求
            UsernamePasswordAuthenticationToken authenticated = UsernamePasswordAuthenticationToken.authenticated(auths[1],null, AuthorityUtils.NO_AUTHORITIES);
            authenticated.setDetails(user);

            // 放入安全上下文中
            SecurityContextHolder.getContext().setAuthentication(authenticated);
        }

        // 放行其它请求
        filterChain.doFilter(request, response);
    }

    /**
     * 验证失败处理
     */
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException exception) throws IOException, ServletException {
        this.authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
    }
}
